#ifndef _DD_MAN_H_
#define _DD_MAN_H_

#include "dd_base.h"
#include "dd_line.h"
#include "dd_box.h"
#include "dd_spline2.h"
#include "dd_sphere.h"
#include "dd_plane.h"
#include "dd_obb.h"
#include "dd_base_2d.h"
#include "dd_line_2d.h"
#include "dd_box_2d.h"
#include "dd_circle_2d.h"

namespace math
{
    class Vec3;
    class Mat4x4;
}

namespace dd 
{

///////////////////////////////////////////////////////
///         DrawDebug system
///	\brief	System to output debug shapes fast
///			to screen, mainly as a way to easily debug
///         visual algoriths, AI, etc.
///
///         All geometries are cached, and then rendered
///         in a single pass. It's recommended that this
///         is done after all other objects are rendered, 
///         but before the 2D systems are rendered.
///////////////////////////////////////////////////////
class Manager
{
public:

    /**
     * Singleton accessor
     */
    static Manager& Instance();

    /**
     * Initialize the dataset
     */
    bool			Init();

    /**
     * Destroy all data
     */
    bool			Shutdown();

    /**
     * Render all primitives entered
     */
	bool			Render3D();

    /**
     * Render all 2D primitives entered
     */
	bool			Render2D();

    /**
     * Reset all buffers. Done automatically by render
     */
	bool			Reset();
	/**
     * Add a sphere to the debug set
     * @param _Pos          Center of sphere
     * @param _Radius       Radius of sphere
     * @param _DrawInverse  Draw inside out ? (good for large spheres)
     * @param _Color        Set the colour of the sphere
     */
	void			AddSphere(const math::Vec3& _Pos, float _Radius, bool _DrawInverse = true, COLOR _Color = ColorRGBA(255, 255, 255));

    /**
     * Add an axis aligned box to the debug set
     * @param _Min          Low X, Y, Z coordinates of the box (i.e. (-5, -5, -5))
     * @param _Max          High X, Y, Z coordinates of the box (i.e. (+5, +5, +5))
     * @param _Color        Set the colour of the sphere
     */
    void			AddBox(const math::Vec3& _Min, const math::Vec3& _Max, COLOR _Color = ColorRGBA(255, 255, 255));
    /**
     * Add a line to the debug set
     * @param _From         Origin 
     * @param _To           Destination
     * @param _Color        Set the colour of the sphere
     * @param _Arrow        Draw an arrow-head at destination ?
     */
    void			AddLine(const math::Vec3& _From, const math::Vec3& _To, COLOR _Color = ColorRGBA(255, 255, 255), bool _Arrow = false);

    /**
     * Add a spline to the debug set
     * @param _pSpline      Spline to visualize
     * @param _dT           Delta between steps (number of segments = (Spline total time / dT)
     * @param _Color        Set the colour of the sphere
     * @param _Arrow        Draw an arrow-head at destination ?
     */
	void			AddSpline2(math::SplineCache2D* _pSpline, float dT = 0.01f, COLOR _Color = ColorRGBA(255, 255, 255), bool _Knots = false, const math::Vec2& Offset = math::Vec2(0,0));

    /**
     * Add a plane to the debug set
     * @param _Pos          Center of plane
     * @param _Size         X and Z define the extents of the plane
     * @param _Plane        Plane equation to visualize
     * @param _Color        Set the colour of the sphere
     * @param _RenderSolid  Render with fill, or just wireframe ?
     */
    void			AddPlane(const math::Vec3& _Pos, const math::Vec3& _Size, const math::Plane& _Plane, COLOR _Color = ColorRGBA(255, 255, 255), bool _RenderSolid = true);

    /**
     * Add an Object Bounding Box to the debug set
     * @param _Size         Extents of Bounding box
     * @param _Mat          Mat4x4 of object
     * @param _Color        Set the colour of the sphere
     */
	void			AddOBB(const math::Vec3& _Size, const math::Mat4x4& _Mat, COLOR _Color = ColorRGBA(255, 255, 255));


	void			AddLine2D(const math::Vec2& From, const math::Vec2& To, COLOR _Color = ColorRGBA(255, 255, 255), bool _Arrow = false);
	void			AddBox2D(const math::Vec2& From, const math::Vec2& To, COLOR _Color = ColorRGBA(255, 255, 255), bool IsFilled = false);
	void			AddCircle2D(const math::Vec2& Pos, float Rad, COLOR _Color = ColorRGBA(255, 255, 255), bool IsFilled = false);
	void			AddCapsule2D(const math::Vec2& From, const math::Vec2& To, float Rad, COLOR _Color = ColorRGBA(255, 255, 255));

private:
    /**
     * Creates the Manager. Remember to initialize before using
     */
					Manager();
    /**
     * Destroys the manager
     */
					~Manager();

    /// Not implemented to prevent copying
    Manager( const Manager & );     
    Manager& operator=( const Manager & );
                  

    /// Different primitive types
	enum PRIMITIVE_TYPE 
	{
		Sphere,
		Line,
		Box,
		Plane,
		OBB,
		Line2D,
		Box2D,
		Circle2D,
		Spline2,
		PRIMITIVE_COUNT
	};

    /// number of primitives we keep for each type
	enum PRIMITIVE_COUNT
	{
		SPHERE_COUNT       = 200,
		LINE_COUNT         = 5000,
		BOX_COUNT          = 10000,
		PLANE_COUNT        = 200,
		OBB_COUNT          = 200,
		LINE_2D_COUNT	   = 5000,
		BOX_2D_COUNT	   = 4000,
		CIRCLE_2D_COUNT	   = 1000,
		SPLINE2_COUNT      = 400
	};


	int				m_UsedA[PRIMITIVE_COUNT];           /// How many of each primitive class are we using ?
	DBG_BUFFER	    m_Buffer3D;                           /// Access to a rendering buffer
	DBG_BUFFER_2D	m_Buffer2D;                           /// Access to a rendering buffer

    dd::Line		m_LineA[LINE_COUNT];                /// Line Primitive set
	dd::Box		    m_BoxA[BOX_COUNT];                  /// Box Primitive set
	dd::Sphere	    m_SphereA[SPHERE_COUNT];            /// Sphere Primitive set
	dd::Plane	    m_PlaneA[PLANE_COUNT];              /// Plane Primitive set
	dd::OBB		    m_OBBA[OBB_COUNT];                  /// OBB Primitive set
	dd::Line2D		m_Line2DA[LINE_2D_COUNT];
	dd::Box2D		m_Box2DA[BOX_2D_COUNT];
	dd::Circle2D	m_Circle2DA[CIRCLE_2D_COUNT];
	dd::Spline2	    m_Spline2A[SPLINE2_COUNT];            /// Spline Primitive set
};

}// End namespace dd

#endif 